package org.van.blog.controller;

import java.net.URLEncoder;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.github.pagehelper.PageInfo;
import com.vdurmont.emoji.EmojiParser;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.van.blog.constant.ErrorConstant;
import org.van.blog.constant.Types;
import org.van.blog.constant.WebConst;
import org.van.blog.dto.MetaDto;
import org.van.blog.dto.cond.ContentCond;
import org.van.blog.exception.BusinessException;
import org.van.blog.model.CommentDomain;
import org.van.blog.model.ContentDomain;
import org.van.blog.model.MetaDomain;
import org.van.blog.service.article.ContentService;
import org.van.blog.service.comment.CommentService;
import org.van.blog.service.meta.MetaService;
import org.van.blog.utils.APIResponse;
import org.van.blog.utils.IPKit;
import org.van.blog.utils.TaleUtils;

@Controller
public class HomeController extends BaseController {

    @Autowired
    private ContentService contentService;

    @Autowired
    private CommentService commentService;

    @Autowired
    private MetaService metaService;

    @GetMapping(value = "/")
    public String index(

            HttpServletRequest request,

            @RequestParam(name = "page", required = false, defaultValue = "1") int page,

            @RequestParam(name = "limit", required = false, defaultValue = "5") int limit) {
        PageInfo<ContentDomain> articles = contentService.getArticlesByCond(new ContentCond(), page, limit);

        request.setAttribute("articles", articles);
        return "blog/home";
    }

    @GetMapping(value = "/archives")
    public String archives(
            HttpServletRequest request,
            @RequestParam(name = "page", required = false, defaultValue = "1") int page,
            @RequestParam(name = "limit", required = false, defaultValue = "10") int limit) {
        PageInfo<ContentDomain> articles = contentService.getArticlesByCond(new ContentCond(), page, limit);
        request.setAttribute("articles", articles);
        return "blog/archives";
    }

    @GetMapping(value = "/categories")
    public String categories(HttpServletRequest request) {
        List<MetaDto> categories = metaService.getMetaList(Types.CATEGORY.getType(), null, WebConst.MAX_POSTS);
        Long categoryCount = metaService.getMetasCountByType(Types.CATEGORY.getType());
        request.setAttribute("categories", categories);
        request.setAttribute("categoryCount", categoryCount);
        return "blog/category";
    }

    @GetMapping(value = "/categories/{name}")
    public String categoriesDetail(
            HttpServletRequest request,
            @PathVariable("name") String name) {
        MetaDomain category = metaService.getMetaByName(Types.CATEGORY.getType(), name);
        if (null == category.getName())
            throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY);
        List<ContentDomain> articles = contentService.getArticleByCategory(category.getName());
        request.setAttribute("category", category.getName());
        request.setAttribute("articles", articles);
        return "blog/category_detail";
    }

    @GetMapping(value = "/tags")
    public String tags(HttpServletRequest request) {
        List<MetaDto> tags = metaService.getMetaList(Types.TAG.getType(), null, WebConst.MAX_POSTS);
        Long tagCount = metaService.getMetasCountByType(Types.TAG.getType());
        request.setAttribute("tags", tags);
        request.setAttribute("tagCount", tagCount);
        return "blog/tags";
    }

    @GetMapping(value = "/tags/{name}")
    public String tagsDetail(
            HttpServletRequest request,
            @PathVariable("name") String name) {
        MetaDomain tags = metaService.getMetaByName(Types.TAG.getType(), name);
        List<ContentDomain> articles = contentService.getArticleByTags(tags);
        request.setAttribute("articles", articles);
        request.setAttribute("tags", tags.getName());
        return "blog/tags_detail";
    }

    @GetMapping(value = "/about")
    public String about() {
        return "blog/about";
    }

    @GetMapping(value = "/detail/{cid}")
    public String detail(
            @PathVariable("cid") Integer cid,
            HttpServletRequest request) {
        ContentDomain article = contentService.getArticleById(cid);
        request.setAttribute("article", article);

        this.updateArticleHits(article.getCid(), article.getHits());

        List<CommentDomain> comments = commentService.getCommentsByCId(cid);
        request.setAttribute("comments", comments);

        return "blog/detail";
    }

    private void updateArticleHits(Integer cid, Integer chits) {
        Integer hits = cache.hget("article", "hits");
        if (chits == null) {
            chits = 0;
        }
        hits = null == hits ? 1 : hits + 1;
        if (hits >= WebConst.HIT_EXEED) {
            ContentDomain temp = new ContentDomain();
            temp.setCid(cid);
            temp.setHits(chits + hits);
            contentService.updateContentByCid(temp);
            cache.hset("article", "hits", 1);
        } else {
            cache.hset("article", "hits", hits);
        }
    }

    @PostMapping(value = "/comment")
    @ResponseBody
    public APIResponse<?> comment(HttpServletRequest request, HttpServletResponse response,
            @RequestParam(name = "cid", required = true) Integer cid,
            @RequestParam(name = "coid", required = false) Integer coid,
            @RequestParam(name = "author", required = false) String author,
            @RequestParam(name = "email", required = false) String email,
            @RequestParam(name = "url", required = false) String url,
            @RequestParam(name = "content", required = true) String content,
            @RequestParam(name = "csrf_token", required = true) String csrf_token) {

        String ref = request.getHeader("Referer");
        if (StringUtils.isBlank(ref) || StringUtils.isBlank(csrf_token)) {
            return APIResponse.fail("Access Failed!");
        }

        String token = cache.hget(Types.CSRF_TOKEN.getType(), csrf_token);
        if (StringUtils.isBlank(token)) {
            return APIResponse.fail("Access Failed!");
        }

        if (null == cid || StringUtils.isBlank(content)) {
            return APIResponse.fail("Please input your comment!");
        }

        if (StringUtils.isNotBlank(author) && author.length() > 50) {
            return APIResponse.fail("Name too long!");
        }

        if (StringUtils.isNotBlank(email) && !TaleUtils.isEmail(email)) {
            return APIResponse.fail("E-mail format error!");
        }

        if (StringUtils.isNotBlank(url) && !TaleUtils.isURL(url)) {
            return APIResponse.fail("Website format error!");
        }

        if (content.length() > 200) {
            return APIResponse.fail("Comment within 200 words!");
        }

        String val = IPKit.getIpAddressByRequest1(request) + ":" + cid;
        Integer count = cache.hget(Types.COMMENTS_FREQUENCY.getType(), val);
        if (null != count && count > 0) {
            return APIResponse.fail("Please try again later!");
        }

        author = TaleUtils.cleanXSS(author);
        content = TaleUtils.cleanXSS(content);

        author = EmojiParser.parseToAliases(author);
        content = EmojiParser.parseToAliases(content);

        CommentDomain comments = new CommentDomain();
        comments.setAuthor(author);
        comments.setCid(cid);
        comments.setIp(request.getRemoteAddr());
        comments.setUrl(url);
        comments.setContent(content);
        comments.setEmail(email);
        comments.setParent(coid);

        try {
            commentService.addComment(comments);
            cookie("tale_remember_author", URLEncoder.encode(author, "UTF-8"), 7 * 24 * 60 * 60, response);
            cookie("tale_remember_mail", URLEncoder.encode(email, "UTF-8"), 7 * 24 * 60 * 60, response);
            if (StringUtils.isNotBlank(url)) {
                cookie("tale_remember_url", URLEncoder.encode(url, "UTF-8"), 7 * 24 * 60 * 60, response);
            }
            cache.hset(Types.COMMENTS_FREQUENCY.getType(), val, 1, 30);

            return APIResponse.success();

        } catch (Exception e) {
            throw BusinessException.withErrorCode(ErrorConstant.Comment.ADD_NEW_COMMENT_FAIL);
        }

    }

    private void cookie(String name, String value, int maxAge, HttpServletResponse response) {
        Cookie cookie = new Cookie(name, value);
        cookie.setMaxAge(maxAge);
        cookie.setSecure(false);
        response.addCookie(cookie);
    }

}
